/* 
    定义 面向对象轮播图构造函数
        参数1   生成轮播图的标签对象
        参数2   生成轮播图的数组数据

*/

class SetBanner {
    constructor(element, imgArr) {
        // 定义属性 存储 形参数据
        this.ele = element;
        this.arr = imgArr;
        // 定义属性 存储全局变量
        this.ul;
        this.ol;
        this.ulLis;
        this.olLis;
        this.liWidth;
        this.length = imgArr.length + 2;
        this.time;
        this.index = 1;
        this.flag = true;
    }

    // 定义入口函数 调用所有需要执行的函数程序
    init() {
        // 动态生成页面
        this.setPage();
        // 自动轮播
        this.autoLoop();
        // 鼠标移入移出
        this.mouseEvent();
        // 点击事件
        this.setClick();
        // 浏览器最小化
        this.hide();
    }

    // 定义函数 动态生成页面
    setPage() {
        // 生成 ul>li ol>li div>a

        // 创建 ul ol 标签节点
        this.ul = document.createElement('ul');
        this.ol = document.createElement('ol');

        // 定义变量 存储 ul>li ol>li 字符串
        let ulStr = '';
        let olStr = '';

        // 循环遍历数组 动态生成 ul>li ol>li
        this.arr.forEach((item, key) => {
            // item 是 数组中 数据单元存储的数据  也就是 对象
            // ul > li > img > src --- 路径+item的name
            ulStr += `<li><img class="banner1" src="${item.name}" alt=""></li>`;

            // 第一个 ol>li 有 class,active
            olStr += key === 0 ? `<li name="olLi" num="${key}" class="active"></li>` : `<li name="olLi" num="${key}"></li>`;
        })

        // 将 ul>li ol>li 字符串 写入 ul ol 标签对象
        this.ul.innerHTML = ulStr;
        this.ol.innerHTML = olStr;

        // 创建div标签对象
        const oDiv = document.createElement('div');
        // 定义div标签的内容
        oDiv.innerHTML = '<a name="left" href="JavaScript:;">&lt;</a><a name="right" href="JavaScript:;">&gt;</a>';

        // 将 ul ol div 标签 写入 banner,div标签中
        this.ele.appendChild(this.ul);
        this.ele.appendChild(this.ol);
        this.ele.appendChild(oDiv);

        // 克隆标签对象 获取 所有的 ul>li ol>li 
        this.ulLis = this.ul.querySelectorAll('li');
        this.olLis = this.ol.querySelectorAll('li');

        // 克隆 原始ul>li 中的 第一个和最后一个
        const cloneFirst = this.ulLis[0].cloneNode(true);
        const cloneLast = this.ulLis[this.ulLis.length - 1].cloneNode(true);

        // 将 克隆的第一个li 写入 ul 的末位 
        this.ul.appendChild(cloneFirst);

        // 将 克隆的最后一个li 写入 ul 的起始
        this.ul.insertBefore(cloneLast, this.ulLis[0]);

        // 重新设定 ul标签的宽度 
        // 当前li的个数 * li的宽度
        // li的个数 原始数组长度+2 
        // 获取li标签宽度
        this.liWidth = parseInt(window.getComputedStyle(this.ulLis[0]).width);

        // 设定 ul的宽度
        this.ul.style.width = (this.liWidth * this.length) + 'px';

        // 当期显示的是 ul>li 的第一个 也就是 克隆的原始轮播图最后一张
        // ul 向左定位一个li宽度 显示 当前ul>li 第二个 也就是 原始轮播图 第一张
        this.ul.style.left = -this.liWidth + 'px';

    }


    // 定义函数 实现自动轮播
    autoLoop() {
        // 通过定时器 实现 ul标签定位的改变
        this.time = setInterval(() => {
            // 给 index 变量 ++
            this.index++;

            // 通过调用函数 设定 焦点按钮样式
            this.setFocusStyle();

            // ul标签定位的数值是 -li的宽度 * index
            // 回调函数 是 move() 运动执行停止时 触发的函数程序

            // 赋值的是函数名称 
            // 通过设定bind() 生成一个新的函数 同时设定this指向

            // this.loopEnd() 原始要执行的函数 
            // 需要生成一个新的函数 同时改变this指向
            // 新的this指向 就是 当前的this指向 就是 实例化对象
            // const newFun = this.loopEnd.bind( this );
            // move(this.ul , { left: -this.liWidth * this.index }, this.loopEnd.bind( this ) );

            // 使用 匿名函数 调用 回调函数 
            // 将 匿名函数 修改为 箭头函数 
            // 箭头函数的this指向 是 固定的不能改变的 只能是 父级程序的this指向
            // 赋值的箭头函数 的 this 指向 永远是 父级程序的this指向 
            // 也就是 当前程序的this指向 也就是 实例化对象
            move(this.ul, { left: -this.liWidth * this.index }, () => this.loopEnd());

            
        }, 4000)
    }

    // 定义函数 运动结束触发的回调函数
    loopEnd() {
        // 判断 index的数值 是 当前 最后一个li的索引下标时
        // 给 index 赋值 1 也就是 定位 显示 当前第二个li 也就是 原始第一个轮播图

        // 回调函数的this指向会彻底改变
        // 任何位置的this都不是实例化对象
        // console.log( this );

        if (this.index === this.length - 1) {
            // 当 index 是 当前最后一个li的索引下标
            // 给 index 赋值 1  
            this.index = 1;

            // 按照 index的数值 将 ul标签 瞬间定位
            this.ul.style.left = -this.liWidth * this.index + 'px';

        } else if (this.index === 0) {
            // 当 index 是 0 也就是 显示的当前ul>li的第一个 也就是 原始轮播图的最后一个
            // 给 index 赋值 倒数第二个li的索引下标
            this.index = this.length - 2;

            // 按照 index的数值 将 ul标签 瞬间定位
            this.ul.style.left = -this.liWidth * this.index + 'px';
        }

        // 运动结束 给 flag变量赋值 true
        // 可以再次调用 move() 执行运动函数
        this.flag = true;

    }

    // 定义函数 切换 焦点按钮css样式
    setFocusStyle() {
        // 清除所有 焦点按钮 class,active
        this.olLis.forEach((item) => {
            // item 就是 oOlLis数组中存储的数据数值 也就是 ol>li标签
            item.classList.remove('active');
        })

        // 给 ul>li 对应的 ol>li 添加 class,active

        if (this.index === this.length - 1) {
            // 当 index 是 length-1 也就是 显示的 当前ul>li中的最后一个li
            // 对应的 ol>li 是 第一个 ol>li
            this.olLis[0].classList.add('active');

        } else if (this.index === 0) {
            // 当 index 是 0 也就是 显示的 当前ul>li的第一个li
            // 对应的 ol>li 是 最后一个 ol>li
            this.olLis[this.olLis.length - 1].classList.add('active');


        } else {
            // index-1 是 当前显示的ul>li 对应的 ol>li 的索引下标 
            this.olLis[this.index - 1].classList.add('active');
        }

    }



    // 定义函数 鼠标的移入移出
    mouseEvent() {
        // 给 父级div标签 添加 mouseenter 事件
        this.ele.addEventListener('mouseenter', () => {
            // 清除定时器
            clearInterval(this.time);
        })

        // 给 父级div标签 添加 mouseleave 事件
        this.ele.addEventListener('mouseleave', () => {
            // 再次调用自动轮播函数
            this.autoLoop();
        })

    }

    // 定义函数 设定点击事件
    setClick() {

        // 给父级div添加点击事件
        this.ele.addEventListener('click', (event) => {



            // 通过 event.target 判断 点击的标签 触发不同的程序
            if (event.target.getAttribute('name') === 'left') {
                // 防止点击过快的判断
                if (this.flag) {
                    this.flag = false;
                } else {
                    return;
                }



                // 显示 上一张轮播图 也就是 index累减1
                this.index--;

                // 通过调用函数 设定 焦点按钮样式
                this.setFocusStyle();

                // 根据 新的index数值 重新定位ul标签
                // 通过 move()函数 实现 运动切换
                move(this.ul, { left: -this.liWidth * this.index }, this.loopEnd.bind(this));

            } else if (event.target.getAttribute('name') === 'right') {
                // 防止点击过快的判断
                if (this.flag) {
                    this.flag = false;
                } else {
                    return;
                }


                // 显示 下一张轮播图 也就是 index累加1
                this.index++;

                // 通过调用函数 设定 焦点按钮样式
                this.setFocusStyle();

                // 根据 新的index数值 重新定位ul标签
                // 通过 move()函数 实现 运动切换
                move(this.ul, { left: -this.liWidth * this.index }, this.loopEnd.bind(this));


            } else if (event.target.getAttribute('name') === 'olLi') {
                // 防止点击过快的判断
                if (this.flag) {
                    this.flag = false;
                } else {
                    return;
                }

                // 获取 当前点击的ol>li标签 num属性存储的索引下标
                // 获取结果 +1 是 对应的 ul>li 的索引下标
                // 将 结果 赋值 给 index变量储存
                // 需要转化为数值类型 再 加1
                this.index = Number(event.target.getAttribute('num')) + 1;

                // 通过调用函数 设定 焦点按钮样式
                this.setFocusStyle();

                // 根据 新的index数值 重新定位ul标签
                // 通过 move()函数 实现 运动切换
                move(this.ul, { left: -this.liWidth * this.index }, this.loopEnd.bind(this));


            }
        })
    }

    // 定义函数 设定浏览器显示最小化事件
    hide() {
        // 给 整个 html文档 添加事件
        document.addEventListener('visibilitychange', () => {
            // 当浏览器显示状态改变时 触发 判断 

            if (document.visibilityState === 'hidden') {
                // 当 浏览器显示状态码 是 hidden 也就是 浏览器最小化 
                // 清除定时器
                clearInterval(this.time);

            } else if (document.visibilityState === 'visible') {
                // 当 浏览器显示状态码 是 visible 也就是 浏览器显示
                // 再次调用自动轮播函数
                this.autoLoop();
            }

        })
    }
}
